#include "Arduino.h"
#include "espsettings.h"
#include "espuser.h"
#include "INA226.h"
#include "Wire.h"
#include <algorithm>
#include "espwebsocket.h"
#include <AutoPID.h>
#include "OneButton.h"

#define PIN_PWM 13
#define PIN_KEY 14
#define PIN_FAN 15
#define PIN_LED 2
#define PIN_TEMP A0

#define SAMPLING_TIMES 10
#define TEMP_READ_DELAY 50
#define OUTPUT_MIN 0
#define OUTPUT_MAX 1024

OneButton button(PIN_KEY, false);
INA226 INA(0x40);

double voltage = 0;
double current = 0;
double power = 0;

bool heating = false;

double Kp = 50;
double Ki = 0.01;
double Kd = 0;
double temperature, setPoint, outputVal;
AutoPID myPID(&temperature, &setPoint, &outputVal, OUTPUT_MIN, OUTPUT_MAX, Kp, Ki, Kd);
uint64_t lastTempUpdate;

void EspUser::setHeating(DynamicJsonDocument doc)
{
    heating = doc["heating"];
}
void EspUser::setTemp(DynamicJsonDocument doc)
{
    setPoint = doc["temp"];
    String strPoint = "";
    strPoint.concat(int(setPoint));
    log_debug("setPoint:%s", strPoint.c_str());
    Settings::writeString(KEY_HEATING_TEMPERATURE, strPoint.c_str());
}
void clear()
{
    voltage = -1;
    current = -1;
    power = -1;
    temperature = 0;
}
void getTemperature()
{

    int i = SAMPLING_TIMES;
    int32_t t = 0;
    while (i--)
    {
        t += analogRead(PIN_TEMP);
    }
    t = t / SAMPLING_TIMES;
    // 计算adc电压
    temperature = 1.0 * t / 1024;
    // 计算实际阻值
    temperature = 5100 * temperature / (3.3 - temperature);
    // 计算对应温度,公式由pt1000温度阻值对照表拟合
    temperature = (temperature - 1004.9) / 3.7351;
}
void getPower()
{
    double u, c;
    uint8_t i = 5;
    while (i--)
    {
        u = INA.getBusVoltage();
        c = INA.getCurrent();
        current = current == -1 ? c : max(c, current);
        voltage = voltage == -1 ? u : min(u, voltage);
    }
    power = INA.getPower();
}
void buttonMulti()
{
    //三连击，长按因触摸会出现误报
    if (button.getNumberClicks() == 3)
    {
        heating = !heating;
        log_debug("button:%d", heating);
    }
}
DynamicJsonDocument EspUser::getState()
{
    DynamicJsonDocument doc(200);
    doc["cmd"] = "state";
    doc["state"] = heating;
    doc["targetTemp"] = setPoint;
    doc["I"] = current;
    doc["U"] = voltage;
    doc["P"] = power;
    doc["T"] = temperature;
    return doc;
}
void EspUser::begin()
{
    pinMode(PIN_A0, INPUT);
    pinMode(PIN_PWM, OUTPUT);
    pinMode(PIN_LED, OUTPUT);
    pinMode(PIN_FAN, OUTPUT);
    pinMode(PIN_KEY, INPUT);

    digitalWrite(PIN_FAN, HIGH);

    String strPoint = Settings::readString(KEY_HEATING_TEMPERATURE);
    log_debug("strPoint:%s", strPoint.c_str());
    setPoint = strPoint.toInt();
    EspWebsocket::attach("heating", EspUser::setHeating);
    EspWebsocket::attach("setTemp", EspUser::setTemp);

    button.attachMultiClick(buttonMulti);

    Wire.begin();
    if (!INA.begin())
    {
        log_debug("could not connect. Fix and Reboot");
    }
    INA.setMaxCurrentShunt(5, 0.01);

    myPID.setBangBang(10);
    myPID.setTimeStep(100);
}

// Begin which setup everything
void EspUser::handle()
{
    button.tick();

    if (heating)
    {
        myPID.run();
        analogWrite(PIN_PWM, outputVal);
        // log_debug("PWM:%f", outputVal);
    }
    else
    {
        myPID.stop();
        analogWrite(PIN_PWM, OUTPUT_MIN);
    }
    digitalWrite(PIN_LED, myPID.atSetPoint(5));

    if ((millis() - lastTempUpdate) > TEMP_READ_DELAY)
    {
        lastTempUpdate = millis();
        if (lastTempUpdate < 1000)
            log_debug("time:%lld", lastTempUpdate);

        clear();
        getPower();
        getTemperature();

        EspWebsocket::send(getState());
    }
}
